package com.gjl.wechat.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.gjl.common.utils.RedisLock;
import com.gjl.wechat.service.WechatAppletService;
import com.gjl.wechat.service.dto.applet.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


@Slf4j
@Service
public class WechatAppletServiceImpl implements WechatAppletService {

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisLock redisLock;

    /**
     * 登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程
     * 转换unionid
     *
     * @param jsCode     登录时获取的 code，可通过wx.login获取
     * @param appletInfo 小程序信息
     * @return 小程序登录后信息
     */
    @Override
    public JsCode2SessionRes jscode2session(String jsCode, AppletDTO appletInfo) {
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("appid", appletInfo.getAppid());
        paramMap.put("secret", appletInfo.getAppSecret());
        paramMap.put("js_code", jsCode);
        HttpHeaders httpheaders = new HttpHeaders();
        httpheaders.setContentType(MediaType.APPLICATION_JSON);
        httpheaders.add("charset", "UTF-8");
        HttpEntity<JSONObject> httpEntity = new HttpEntity<JSONObject>(null, httpheaders);
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={js_code}&grant_type=authorization_code";
        String resultObject = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class, paramMap).getBody();
        JsCode2SessionRes result = JSONObject.parseObject(resultObject).toJavaObject(JsCode2SessionRes.class);
        if (result == null) {

        }
        return result;
    }

    /**
     * 获取小程序accessToken
     *
     * @param appletInfo 小程序信息
     * @return 小程序accessToken
     */
    @Override
    public String getAppletAccessToken(AppletDTO appletInfo) {
        return this.getAppletAccessToken(appletInfo, false);
    }


    /**
     * 获取小程序accessToken
     *
     * @param appletInfo 小程序信息
     * @return 小程序accessToken
     */
    @Override
    public String getAppletAccessToken(AppletDTO appletInfo, boolean refresh) {
        String redisKey = appletInfo.getAppid() + ":applet_access_token";
        String lock = appletInfo.getAppid() + ":applet_access_token_lock";


        // 判断redis中是否有该服务商的provider_access_token
        if (!refresh) {
            String accessToken = stringRedisTemplate.opsForValue().get(redisKey);
            log.info("get applet accessToken {}", accessToken);
            if (StringUtils.isNotEmpty(accessToken)) {
                return accessToken;
            }
        }

        // 加锁
        String token = redisLock.lock(lock, 3000, 3500);
        try {
            if (token != null) {
                // 需要重新获取token
                Map<String, String> paramMap = new HashMap<>();
                paramMap.put("appid", appletInfo.getAppid());
                paramMap.put("secret", appletInfo.getAppSecret());
                HttpHeaders httpheaders = new HttpHeaders();
                httpheaders.setContentType(MediaType.APPLICATION_JSON);
                httpheaders.add("charset", "UTF-8");
                HttpEntity<JSONObject> httpEntity = new HttpEntity<JSONObject>(null, httpheaders);
                String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}";
                JSONObject result = restTemplate.exchange(url, HttpMethod.GET, httpEntity, JSONObject.class, paramMap).getBody();
                log.info("获取小程序accessToken {}:", result);
                if (result.containsKey("access_token")) {
                    // 将token放入redis中
                    String accessToken = result.getString("access_token");
                    long expiresIn = result.getLongValue("expires_in") - 300;
                    stringRedisTemplate.opsForValue().set(redisKey, accessToken, expiresIn, TimeUnit.SECONDS);
                    return accessToken;
                }
            }
        } catch (Exception e) {
            log.error("get applet accessToken err ", e);
        } finally {
            // 释放锁
            if (token != null) {
                redisLock.unlock(lock, token);
            }
        }
        return null;
    }

    /**
     * 小程序内容检测
     *
     * @param req        检测内容
     * @param appletInfo 小程序信息
     * @return 检测结果
     */
    @Override
    public AppletMsgSecCheckRes msgSecCheck(AppletMsgSecCheckReq req, AppletDTO appletInfo) {

        HttpHeaders httpheaders = new HttpHeaders();
        httpheaders.setContentType(MediaType.APPLICATION_JSON);
        httpheaders.add("charset", "UTF-8");

        JSONObject paramObject = JSONObject.parseObject(JSONObject.toJSONString(req));
        HttpEntity<JSONObject> httpEntity = new HttpEntity<>(paramObject, httpheaders);

        String accessToken = this.getAppletAccessToken(appletInfo);

        String url = "https://api.weixin.qq.com/wxa/msg_sec_check?access_token=" + accessToken;
        JSONObject result = restTemplate.postForEntity(url, httpEntity, JSONObject.class).getBody();

        if (result == null) {
            log.error("applet msg_sec_check err , result is [{}]", result);
        }

        AppletMsgSecCheckRes res = result.toJavaObject(AppletMsgSecCheckRes.class);
        return res;
    }

    /**
     * 该接口用于获取小程序 scheme 码，适用于短信、邮件、外部网页、微信内等拉起小程序的业务场景
     * <p>1. 每个 URL Scheme 或 URL Link 有效期最长 30 天，均不再支持永久有效的链接、不再区分短期有效链接与长期有效链接；</p>
     * <p>2. 链接生成后，若在微信外打开，用户可以在浏览器页面点击进入小程序。每个独立的链接被用户访问后，仅此用户可以再次访问并打开对应小程序，
     * 其他用户无法再次通过相同链接打开该小程序；</p>
     * <p>3. 单个小程序每天生成链接数（URL Scheme 和 URL Link 总数）上限为 50 万条。</p>
     *
     * @param req        小程序地址参数
     * @param appletInfo 小程序信息
     * @return urlScheme
     */
    @Override
    public AppletSchemeRes generatescheme(AppletSchemeReq req, AppletDTO appletInfo) {
        String accessToken = this.getAppletAccessToken(appletInfo);

        JSONObject paramObject = JSONObject.parseObject(JSONObject.toJSONString(req));

        HttpHeaders httpheaders = new HttpHeaders();
        httpheaders.add("charset", "UTF-8");
        httpheaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<JSONObject> httpEntity = new HttpEntity<>(paramObject, httpheaders);
        String url = "https://api.weixin.qq.com/wxa/generatescheme?access_token=" + accessToken;
        JSONObject result = restTemplate.postForEntity(url, httpEntity, JSONObject.class).getBody();

        AppletSchemeRes res = result.toJavaObject(AppletSchemeRes.class);
        if (Objects.equals(res.getErrcode(), WeChatApiErrorCode.JK_SUCCESS)) {
            return res;
        } else if (Objects.equals(res.getErrcode(), "40001")) {
            // 如果过期，则递归调用一次
            this.getAppletAccessToken(appletInfo, true);
            return this.generatescheme(req, appletInfo);
        } else {
            log.error("generatescheme err , result is {}", result.toJSONString());
            //throw new WeChatException(WechatExceptionConsts.APPLET_GENERATESCHEME_ERR);
        }
        return new AppletSchemeRes();
    }


    @Override
    public String getstableAppletAccessToken(AppletDTO appletInfo, boolean refresh) {
        String redisKey = appletInfo.getAppid() + ":applet_stable_access_token";
        if (!refresh) {
            String accessToken = stringRedisTemplate.opsForValue().get(redisKey);
            if (StringUtils.isNotEmpty(accessToken)) {
                return accessToken;
            }
        }

        String lock = appletInfo.getAppid() + ":applet_stable_access_token_lock";
        // 加锁
        String token = redisLock.lock(lock, 3000, 3500);
        try {
            if (token != null) {
                // 需要重新获取token
                JSONObject paramObject = new JSONObject();
                paramObject.put("appid", appletInfo.getAppid());
                paramObject.put("secret", appletInfo.getAppSecret());
                paramObject.put("grant_type", "client_credential");
                paramObject.put("force_refresh", refresh);
                HttpHeaders httpheaders = new HttpHeaders();
                httpheaders.add("charset", "UTF-8");
                httpheaders.setContentType(MediaType.APPLICATION_JSON);
                HttpEntity<JSONObject> httpEntity = new HttpEntity<>(paramObject, httpheaders);
                String url = "https://api.weixin.qq.com/cgi-bin/stable_token";
                JSONObject result = restTemplate.postForEntity(url, httpEntity, JSONObject.class).getBody();
                String accessToken = result.getString("access_token");
                long expiresIn = result.getLongValue("expires_in") - 300;
                stringRedisTemplate.opsForValue().set(redisKey, accessToken, expiresIn, TimeUnit.SECONDS);
                return accessToken;
            }
        } catch (Exception e) {
            log.error("get applet accessToken err ", e);
        } finally {
            // 释放锁
            if (token != null) {
                redisLock.unlock(lock, token);
            }
        }
        return null;
    }

    /**
     * 获取小程序收取手机号
     *
     * @param code       手机号获取凭证
     * @param appletInfo 小程序信息
     */
    @Override
    public AppletPhoneNumberRes getuserphonenumber(String code, AppletDTO appletInfo) {
        String accessToken = this.getAppletAccessToken(appletInfo);

        JSONObject paramObject = new JSONObject();
        paramObject.put("code", code);

        HttpHeaders httpheaders = new HttpHeaders();
        httpheaders.add("charset", "UTF-8");
        httpheaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<JSONObject> httpEntity = new HttpEntity<>(paramObject, httpheaders);
        String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;
        JSONObject result = restTemplate.postForEntity(url, httpEntity, JSONObject.class).getBody();

        AppletPhoneNumberRes res = result.toJavaObject(AppletPhoneNumberRes.class);
        if (Objects.equals(res.getErrcode(), WeChatApiErrorCode.JK_SUCCESS)) {
            return res;
        } else if (Objects.equals(res.getErrcode(), "40001")) {
            // 如果过期，则递归调用一次
            this.getAppletAccessToken(appletInfo, true);
            return this.getuserphonenumber(code, appletInfo);
        } else {
            log.error("getuserphonenumber err , result is {}", result.toJSONString());
        }
        return null;
    }

    @Override
    public void sendAppMessage(AppMsg msg, AppletDTO appletDTO, RestTemplate restTemplate) {
        String accessToken = this.getAppletAccessToken(appletDTO);
        JSONObject param = JSONObject.parseObject(JSONObject.toJSONString(msg));

        //param.put("miniprogram_state", "trial");//跳转小程序类型：developer为开发版；trial为体验版；formal为正式版；默认为正式版
        log.info(param.toJSONString());
        HttpHeaders httpheaders = new HttpHeaders();
        httpheaders.setContentType(MediaType.APPLICATION_JSON);
        httpheaders.add("charset", "UTF-8");
        HttpEntity<JSONObject> httpEntity = new HttpEntity<>(param, httpheaders);
        JSONObject result = restTemplate.postForEntity("https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken, httpEntity, JSONObject.class).getBody();
        log.info("发服务消息：{}", result.toJSONString());
        int errcode = result.getIntValue("errcode");
        if (errcode != 0) {
            log.error(result.toJSONString());
        }
        if (errcode == 40001) {// 如果过期，则递归调用一次
            this.getAppletAccessToken(appletDTO, true);
            sendAppMessage(msg, appletDTO, restTemplate);
        }
    }

    @Override
    public byte[] getwxacodeunlimit(AppletDTO appletDTO, String scene, String page) {
        String accessToken = this.getAppletAccessToken(appletDTO);
        JSONObject paramMap = new JSONObject();
        paramMap.put("scene", scene);
        paramMap.put("page", page);
        //todo 上线要改 release
        paramMap.put("env_version", "trial");
        paramMap.put("check_path", false);
        HttpHeaders httpheaders = new HttpHeaders();
        httpheaders.setContentType(MediaType.APPLICATION_JSON);
        httpheaders.add("charset", "UTF-8");
        HttpEntity<JSONObject> httpEntity = new HttpEntity<JSONObject>(paramMap, httpheaders);
        ResponseEntity<byte[]> responseEntity = restTemplate.postForEntity("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken, httpEntity, byte[].class);
        int statusCodeValue = responseEntity.getStatusCodeValue();
        if (statusCodeValue != HttpURLConnection.HTTP_OK) {
        }
        byte[] resBody = responseEntity.getBody();
        return resBody;
    }
}
